package com.shkco.xbjcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.shkco.exception.GlobalExceptionCode;
import com.shkco.result.JSONResult;
import com.shkco.utils.AssertUtil;
import com.shkco.utils.CodeGenerateUtils;
import com.shkco.xbjcc.domain.Course;
import com.shkco.xbjcc.domain.CourseMarket;
import com.shkco.xbjcc.domain.CourseOrder;
import com.shkco.xbjcc.domain.CourseOrderItem;
import com.shkco.xbjcc.dto.*;
import com.shkco.xbjcc.feign.CourseService;
import com.shkco.xbjcc.mapper.CourseOrderMapper;
import com.shkco.xbjcc.service.ICourseOrderItemService;
import com.shkco.xbjcc.service.ICourseOrderService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.shkco.xbjcc.vo.CourseInfoVo;
import com.shkco.xbjcc.vo.CourseOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private CourseService courseService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private ICourseOrderItemService courseOrderItemService;

    private String redisOrderToken = "orderToken:%s:%s";

    private String title = "购买课程 [%s] , 支付[%s] 元";

    @Override
    public String placeOrder(CourseOrderDto courseOrderDto) {
        List<Long> courseIds = courseOrderDto.getCourseIds();
        String token = courseOrderDto.getToken();
        Integer payType = courseOrderDto.getPayType();
        String ids = courseIds.stream().map(id -> String.valueOf(id)).collect(Collectors.joining(","));
        Long loginId = 3L;
        String redisKey = String.format(redisOrderToken, loginId, ids);
        Object orderToken = redisTemplate.opsForValue().get(redisKey);
        AssertUtil.isNotNull(orderToken, GlobalExceptionCode.ERROR);
        boolean boo = token.equals(orderToken.toString());
        AssertUtil.isTrue(boo, GlobalExceptionCode.ERROR);
        JSONResult info = courseService.info(ids);
        AssertUtil.isTrue(info.isSuccess(), GlobalExceptionCode.ERROR);
        Object data = info.getData();
        AssertUtil.isNotNull(data, GlobalExceptionCode.ERROR);
        CourseInfoVo courseInfoVo = JSON.parseObject(JSON.toJSONString(data), CourseInfoVo.class);

        Date now = new Date();
        List<CourseOrderVo> courseInfos = courseInfoVo.getCourseInfos();
        BigDecimal totalAmount = courseInfoVo.getTotalAmount();
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(now);
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        courseOrder.setPayType(payType);
        courseOrder.setStatusOrder(CourseOrder.TO_BE_PAID);
        courseOrder.setTotalAmount(totalAmount);
        courseOrder.setTotalCount(courseInfos.size());
        courseOrder.setUserId(loginId);
        courseOrder.setVersion(0);
//        insert(courseOrder);
        for (int i = 0; i < courseInfos.size(); i++) {
            Course course = courseInfos.get(i).getCourse();
            CourseMarket courseMarket = courseInfos.get(i).getCourseMarket();
            CourseOrderItem courseOrderItem = new CourseOrderItem();
            courseOrderItem.setAmount(courseMarket.getPrice());
            courseOrderItem.setCount(1);
            courseOrderItem.setCourseId(course.getId());
            courseOrderItem.setCourseName(course.getName());
            courseOrderItem.setCoursePic(course.getPic());
            courseOrderItem.setCreateTime(now);
            courseOrderItem.setOrderNo(courseOrder.getOrderNo());
            courseOrderItem.setVersion(0);
            courseOrder.getCourseOrderItems().add(courseOrderItem);
//            courseOrderItemService.insert(courseOrderItem);
            String format = String.format(title, course.getName(), totalAmount);
            courseOrder.setTitle(format);
        }
//        updateById(courseOrder);
        PayDto payDto = new PayDto();
        BeanUtils.copyProperties(courseOrder,payDto);
        PayExtParamsDto payExtParamsDto = new PayExtParamsDto(ids,loginId,courseOrder.getOrderNo());
        payDto.setExtParams(JSON.toJSONString(payExtParamsDto));
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();

        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction("tx-course-order-group",
                "tx-course-order-topic:tx-course-order-tag",
                message,
                JSON.toJSONString(courseOrder));

        boolean sendOk = result.getSendStatus() == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendOk,GlobalExceptionCode.PLACE_AN_ORDER_FAILURE);

        redisTemplate.delete(redisKey);

        try {
            // 发送延迟消息
            Message<String> delayMessage = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult sendResult = rocketMQTemplate.syncSend(
                    "course-order-handler-delay-topic:course-order-handler-delay-tag",
                    delayMessage,
                    2000,
                    30);
            if (!sendResult.getSendStatus().equals(SendStatus.SEND_OK)){
                log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            }

        } catch (Exception e) {
            log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            e.printStackTrace();
        }

        return courseOrder.getOrderNo();
    }

    @Override
    public CourseOrder selectByOrderNo(String orderNo) {
        Wrapper<CourseOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        return selectOne(wrapper);
    }

    @Override
    public void saveCourseOrderAndItem(CourseOrder courseOrder) {
        insert(courseOrder);
        courseOrder.getCourseOrderItems().forEach(e->{
            e.setOrderId(courseOrder.getId());
            courseOrderItemService.insert(e);
        });
    }

    @Override
    public String killPlaceOrder(KillCourseOrderDto killCourseOrderDto) {

        String token = killCourseOrderDto.getToken();
        Integer payType = killCourseOrderDto.getPayType();
        String orderNo = killCourseOrderDto.getOrderNo();

        Object o = redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(o,GlobalExceptionCode.ERROR);
        KillPreOrderParamDto killPreOrderParamDto = JSON.parseObject(JSON.toJSONString(o), KillPreOrderParamDto.class);

        Long courseId = killPreOrderParamDto.getCourseId();

        Long loginId = 3L;
        String redisKey = String.format(redisOrderToken, loginId, courseId);
        Object orderToken = redisTemplate.opsForValue().get(redisKey);
        AssertUtil.isNotNull(orderToken, GlobalExceptionCode.ERROR);
        boolean boo = token.equals(orderToken.toString());
        AssertUtil.isTrue(boo, GlobalExceptionCode.ERROR);

        Date now = new Date();
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(now);
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        courseOrder.setPayType(payType);
        courseOrder.setStatusOrder(CourseOrder.TO_BE_PAID);
        courseOrder.setTotalAmount(killPreOrderParamDto.getTotalAmount());
        courseOrder.setTotalCount(killPreOrderParamDto.getTotalCount());
        courseOrder.setUserId(loginId);
        courseOrder.setVersion(0);

        CourseOrderItem courseOrderItem = new CourseOrderItem();
        courseOrderItem.setAmount(killPreOrderParamDto.getTotalAmount());
        courseOrderItem.setCount(1);
        courseOrderItem.setCourseId(killPreOrderParamDto.getKillCourseId());
        courseOrderItem.setCourseName(killPreOrderParamDto.getCourseName());
        courseOrderItem.setCoursePic(killPreOrderParamDto.getCoursePic());
        courseOrderItem.setCreateTime(now);
        courseOrderItem.setOrderNo(orderNo);
        courseOrderItem.setVersion(0);

        courseOrder.getCourseOrderItems().add(courseOrderItem);
        String format = String.format(title, killPreOrderParamDto.getCourseName(), killPreOrderParamDto.getTotalAmount());
        courseOrder.setTitle(format);

        PayDto payDto = new PayDto();
        BeanUtils.copyProperties(courseOrder,payDto);

        PayExtParamsDto payExtParamsDto = new PayExtParamsDto(killPreOrderParamDto.getCourseId().toString(),loginId,courseOrder.getOrderNo());
        payDto.setExtParams(JSON.toJSONString(payExtParamsDto));
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(payDto)).build();

        TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction("tx-course-order-group",
                "tx-course-order-topic:tx-course-order-tag",
                message,
                JSON.toJSONString(courseOrder));

        boolean sendOk = result.getSendStatus() == SendStatus.SEND_OK;
        AssertUtil.isTrue(sendOk,GlobalExceptionCode.PLACE_AN_ORDER_FAILURE);

        redisTemplate.delete(redisKey);

        try {
            // 发送延迟消息
            Message<String> delayMessage = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult sendResult = rocketMQTemplate.syncSend(
                    "course-order-handler-delay-topic:course-order-handler-delay-tag",
                    delayMessage,
                    2000,
                    30);
            if (!sendResult.getSendStatus().equals(SendStatus.SEND_OK)){
                log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            }

        } catch (Exception e) {
            log.error("下单发送延迟消息失败，订单号为：{}", courseOrder.getOrderNo());
            e.printStackTrace();
        }

        return courseOrder.getOrderNo();
    }
}
